package IO.序列化;

import java.io.*;
import java.util.Arrays;

/*
--序列化：
序列化是指把一个Java对象变成二进制内容，本质上就是一个byte[]数组。
一个Java对象要能序列化，必须实现一个特殊的java.io.Serializable接口，它的定义如下：
public interface Serializable {}
Serializable接口没有定义任何方法，它是一个空接口。我们把这样的空接口称为“标记接口”（Marker Interface），实现了标记接口的
类仅仅是给自身贴了个“标记”，并没有增加任何方法。

把一个Java对象变为byte[]数组，需要使用ObjectOutputStream。它负责把一个Java对象写入一个字节流：
*/
/*
public class 序列化 {

   public static void main(String[] args) throws IOException {
       ByteArrayOutputStream buffer = new ByteArrayOutputStream();
       try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {
           // 写入int:
           output.writeInt(12345);
           // 写入String:
           output.writeUTF("Hello");
           // 写入Object:
           output.writeObject(Double.valueOf(123.456));
       }
       System.out.println(Arrays.toString(buffer.toByteArray()));
   }
}
*/
/*
ObjectOutputStream既可以写入基本类型，如int，boolean，也可以写入String（以UTF-8编码），还可以写入实现
了Serializable接口的Object。
因为写入Object时需要大量的类型信息，所以写入的内容很大。

--反序列化
和ObjectOutputStream相反，ObjectInputStream负责从一个字节流读取Java对象：
try (ObjectInputStream input = new ObjectInputStream(...)) {
    int n = input.readInt();
    String s = input.readUTF();
    Double d = (Double) input.readObject();
}
除了能读取基本类型和String类型外，调用readObject()可以直接返回一个Object对象。要把它变成一个特定类型，必须强制转型。

readObject()可能抛出的异常有：

ClassNotFoundException：没有找到对应的Class；
InvalidClassException：Class不匹配。
对于ClassNotFoundException，这种情况常见于一台电脑上的Java程序把一个Java对象，例如，Person对象序列化以后，通过网络传给另一台
电脑上的另一个Java程序，但是这台电脑的Java程序并没有定义Person类，所以无法反序列化。

对于InvalidClassException，这种情况常见于序列化的Person对象定义了一个int类型的age字段，但是反序列化时，Person类定义的age字
段被改成了long类型，所以导致class不兼容。

为了避免这种class定义变动导致的不兼容，Java的序列化允许class定义一个特殊的serialVersionUID静态变量，用于标识Java类的序列
化“版本”，通常可以由IDE自动生成。如果增加或修改了字段，可以改变serialVersionUID的值，这样就能自动阻止不匹配的class版本：

public class Person implements Serializable {
    private static final long serialVersionUID = 2709425275741743919L;
}
要特别注意反序列化的几个重要特点：

反序列化时，由JVM直接构造出Java对象，不调用构造方法，构造方法内部的代码，在反序列化时根本不可能执行。

安全性
因为Java的序列化机制可以导致一个实例能直接从byte[]数组创建，而不经过构造方法，因此，它存在一定的安全隐患。一个精心构造的byte[]数组被反
序列化后可以执行特定的Java代码，从而导致严重的安全漏洞。

实际上，Java本身提供的基于对象的序列化和反序列化机制既存在安全性问题，也存在兼容性问题。更好的序列化方法是通过JSON这样的通用数据结构来实
现，只输出基本类型（包括String）的内容，而不存储任何与代码相关的信息。
*/
/*
//序列化对象Employee e,并写入d:\employee.ser文件
public class 序列化 {
	public static void main(String[] args) throws Exception {
		Employee e = new Employee();
		e.name = "Reyan Ali";
		e.address = "Phokka Kuan, Ambehta Peer";
		e.SSN = 11122333;
		e.number = 101;
		try (FileOutputStream fileOut = new FileOutputStream("d:\\employee.ser");
			ObjectOutputStream out = new ObjectOutputStream(fileOut)) {		
			out.writeObject(e);//对应反序列化的readObject()
			//out.close();
			//fileOut.close();
			System.out.printf("Serialized data is saved in d:\\employee.ser");
		}
	}
}
*/
//一个类的对象要想序列化成功，必须满足两个条件：
//
//1.该类必须实现 java.io.Serializable 接口。
//2.该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的，则该属性必须注明是短暂的。
//
//如果你想知道一个 Java 标准类是否是可序列化的，请查看该类的文档。检验一个类的实例是否能序列化十分简单， 只需要查看该类有没有实
//现 java.io.Serializable接口。
class Employee implements java.io.Serializable {
	public String name;
	public String address;
	public transient int SSN;//不参与序列化
	public int number;

	public void mailCheck() {
		System.out.println("Mailing a check to " + name + " " + address);
	}
}


//反序列化
public class 序列化 {
   public static void main(String [] args) throws Exception{
      Employee e = null;
      try(FileInputStream fileIn = new FileInputStream("d:\\employee.ser");
      		ObjectInputStream in = new ObjectInputStream(fileIn)){
         e = (Employee) in.readObject();
         //in.close();
         //fileIn.close();
      }catch(IOException i){
         i.printStackTrace();
         return;
      }catch(ClassNotFoundException c){
         System.out.println("Employee class not found");
         c.printStackTrace();
         return;
      }
      System.out.println("Deserialized Employee...");
      System.out.println("Name: " + e.name);
      System.out.println("Address: " + e.address);
      System.out.println("SSN: " + e.SSN);//原值为111222333,这里只能输出0，因为该属性没有实现Serializable接口，是短暂的
      System.out.println("Number: " + e.number);
    }
}

//这里要注意以下要点：
//
//readObject() 方法中的 try/catch代码块尝试捕获 ClassNotFoundException 异常。对于 JVM 可以反序列化对象，它必须是
//能够找到字节码的类。如果JVM在反序列化对象的过程中找不到该类，则抛出一个 ClassNotFoundException 异常。
//注意，readObject() 方法的返回值被转化成 Employee 引用。
//当对象被序列化时，属性 SSN 的值为 111222333，但是因为该属性是短暂的，该值没有被发送到输出流。所以反序列化后 Employee 对象
//的 SSN 属性为 0。
